package eolympics.server;

import java.util.List;

import com.google.common.base.Preconditions;

import jexxus.common.Connection;
import jexxus.common.ConnectionListener;
import jexxus.server.ConnectionToClient;
import jexxus.server.Server;
import minigames.IMiniGame;
import data.GameData;
import data.GameState;
import data.IPlayerReadOnly;
import data.Player;
import eolympics.Communicator;
import eolympics.Communicator.MessageType;

public class GameServer
{
    public final static int      PORT                      = 50161;
    public final static int      MAXIMUM_NUMBER_OF_PLAYERS = 16;

    private final ServerListener mServerConnectionListener = new ServerListener();
    private final GameData       mGameData                 = new GameData();
    private final IGameServer    mGameServer               = new GameServerForGameStates();
    private final Server         mServer;

    private IGameServerState     mGameServerState;

    public GameServer(final Server pServer)
    {
        Preconditions.checkNotNull(pServer);

        mServer = pServer;

        pServer.setConnectionListener(mServerConnectionListener);

        mGameServer.changeGameState(new GameServerNotRunningState(mGameServer));
    }

    private class GameServerForGameStates
            implements IGameServer
    {
        @Override
        public void changeGameState( IGameServerState pNewGameServerState )
        {
            mGameServerState = pNewGameServerState;
            GameState newGameState = pNewGameServerState.getGameState();
            mGameData.setGameState(newGameState);
            mGameData.resetReadynessOfHumanPlayers();

            informEveryClientOfNewGameState(newGameState);
        }

        @Override
        public GameData getGameData()
        {
            return mGameData;
        }

        @Override
        public Server getServer()
        {
            return mServer;
        }

        @Override
        public void sendPlayerListChangedToClients()
        {
            List<Connection> clients = mServer.getClients();
            List<IPlayerReadOnly> playerList = mGameData.getReadOnlyPlayerList();

            for( Connection currentConnection : clients )
            {
                Communicator.sendPlayerListToClient(playerList, currentConnection);
            }
        }
    }

    public void addMiniGamesToServerData( final List<IMiniGame> pMiniGames )
    {
        Preconditions.checkNotNull(pMiniGames);
        Preconditions.checkArgument(mGameData.getMiniGames().size() == 0, "Minigames have already been set!");

        mGameData.addMiniGames(pMiniGames);
    }

    private void informEveryClientOfNewGameState( final GameState pNewGameState )
    {
        List<Connection> clients = mServer.getClients();

        for( Connection currentConnection : clients )
        {
            Communicator.sendGameStateToClient(pNewGameState, currentConnection);
        }
    }

    public boolean isRunning()
    {
        return mServer.isRunning();
    }

    public void start()
    {
        Preconditions.checkArgument(mGameData.getMiniGames().size() > 0, "Minigames have not yet been set!");

        mGameServerState.start();
    }

    public void stop()
    {
        if(mGameData.getCurrentMiniGame() != null)
        {
            mGameData.getCurrentMiniGame().stopForServer();
        }
        
        mGameData.reset();

        mServer.shutdown();

        mGameServer.changeGameState(new GameServerNotRunningState(mGameServer));
    }

    public List<Connection> getConnectedClients()
    {
        return mServer.getClients();
    }

    public void addAiPlayer( final Player pAiPlayer )
    {
        mGameServerState.addAiPlayer(pAiPlayer);
    }

    public void removeAiPlayer( final String pAiPlayerName )
    {
        mGameServerState.removeAiPlayer(pAiPlayerName);
    }

    private class ServerListener
            implements ConnectionListener
    {
        @Override
        public void connectionBroken( final Connection pBroken, final boolean pForced )
        {
            Player lostPlayer = mGameData.getPlayerForIp(pBroken.getIP());

            if( lostPlayer == null )
            {
                return;
            }

            mGameData.removePlayer(lostPlayer);

            mGameServer.sendPlayerListChangedToClients();
        }

        @Override
        public void receive( final byte[] pData, final Connection pClient )
        {
            String message = new String(pData);
            MessageType messageType = Communicator.getMessageType(message);

            if( messageType.equals(Communicator.MessageType.PLAYER_NAME) )
            {
                String playerName = Communicator.getPlayerName(message);

                Player newPlayer = new Player();
                newPlayer.setName(playerName);
                String clientIp = pClient.getIP();
                newPlayer.setIp(clientIp);

                mGameServerState.playerWantsToJoin(newPlayer);
            }
            else if( messageType.equals(Communicator.MessageType.PLAYER_READY) )
            {
                Player playerWhoIsReady = mGameData.getPlayerForIp(pClient.getIP());

                mGameServerState.playerIsReady(playerWhoIsReady);
            }
        }

        @Override
        public void clientConnected( final ConnectionToClient pNewClient )
        {
            System.out.println("new client connected: " + pNewClient.getIP());
            mGameServerState.clientConnected(pNewClient);
        }
    }

}
